package com.google.code.chaotic.client.ui;

import static com.google.code.chaotic.client.Constants.LIGHT_BLUE;

import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import com.allen_sauer.gwt.log.client.Log;
import com.google.code.chaotic.client.ClientSessionManager;
import com.google.code.chaotic.client.Util;
import com.google.code.chaotic.dto.CommuterDto;
import com.google.code.chaotic.dto.CommutingHabitDto;
import com.google.code.chaotic.dto.HabitWaypointDto;
import com.google.code.chaotic.rpc.CommuterSvc;
import com.google.code.chaotic.rpc.CommuterSvcAsync;
import com.google.code.chaotic.rpc.JavaSvc;
import com.google.code.chaotic.rpc.JavaSvcAsync;
import com.google.gwt.core.client.GWT;
import com.google.gwt.core.client.JsArray;
import com.google.gwt.dom.client.Style;
import com.google.gwt.dom.client.Style.Float;
import com.google.gwt.dom.client.Style.FontWeight;
import com.google.gwt.dom.client.Style.Unit;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.dom.client.FocusEvent;
import com.google.gwt.event.dom.client.FocusHandler;
import com.google.gwt.event.logical.shared.CloseEvent;
import com.google.gwt.event.logical.shared.CloseHandler;
import com.google.gwt.event.logical.shared.ValueChangeEvent;
import com.google.gwt.event.logical.shared.ValueChangeHandler;
import com.google.gwt.http.client.URL;
import com.google.gwt.i18n.client.DateTimeFormat;
import com.google.gwt.maps.client.MapWidget;
import com.google.gwt.maps.client.geocode.DirectionQueryOptions;
import com.google.gwt.maps.client.geocode.DirectionResults;
import com.google.gwt.maps.client.geocode.Directions;
import com.google.gwt.maps.client.geocode.DirectionsCallback;
import com.google.gwt.maps.client.geocode.DirectionsPanel;
import com.google.gwt.maps.client.geocode.Geocoder;
import com.google.gwt.maps.client.geocode.LocationCallback;
import com.google.gwt.maps.client.geocode.Placemark;
import com.google.gwt.maps.client.geocode.StatusCodes;
import com.google.gwt.maps.client.geocode.Waypoint;
import com.google.gwt.maps.client.geom.LatLng;
import com.google.gwt.maps.client.overlay.Polyline;
import com.google.gwt.user.client.Command;
import com.google.gwt.user.client.DeferredCommand;
import com.google.gwt.user.client.Timer;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.Anchor;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.CaptionPanel;
import com.google.gwt.user.client.ui.CheckBox;
import com.google.gwt.user.client.ui.DialogBox;
import com.google.gwt.user.client.ui.DockLayoutPanel;
import com.google.gwt.user.client.ui.FlexTable;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.HasAlignment;
import com.google.gwt.user.client.ui.HasVerticalAlignment;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Image;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.ListBox;
import com.google.gwt.user.client.ui.Panel;
import com.google.gwt.user.client.ui.PasswordTextBox;
import com.google.gwt.user.client.ui.PopupPanel;
import com.google.gwt.user.client.ui.RadioButton;
import com.google.gwt.user.client.ui.TextArea;
import com.google.gwt.user.client.ui.TextBox;
import com.google.gwt.user.client.ui.FlexTable.FlexCellFormatter;
import com.google.gwt.user.datepicker.client.DateBox;
import com.google.gwt.user.datepicker.client.DateBox.DefaultFormat;

//

public class ProfileDetailsPanel extends DockLayoutPanel {

	public enum State {
		NEW, UPDATE
	}

	private CommuterDto commuterDto = new CommuterDto();

	/**
	 * Creating remote services' proxies to talk to the server-side services.
	 */
	private final JavaSvcAsync javaSvc = GWT.create(JavaSvc.class);
	private final CommuterSvcAsync commuterSvc = GWT.create(CommuterSvc.class);

	private List<Label> labels = new ArrayList<Label>();

	private static final String NATIONALITY_GROUP = "nationality";
	private static final String GENDER_GROUP = "gender";

	private RadioButton malaysianButton = new RadioButton( //
			NATIONALITY_GROUP, "Malaysian");

	private RadioButton otherButton = new RadioButton( //
			NATIONALITY_GROUP, "Other");

	private RadioButton maleButton = new RadioButton( //
			GENDER_GROUP, "Male");

	private RadioButton femaleButton = new RadioButton( //
			GENDER_GROUP, "Female");

	private static final String IC_TEXT = "I/C no.:";
	private static final String PASSPORT_TEXT = "Passport no.:";

	//

	private Label firstNameLabel = new Label("First name:");
	private TextBox firstNameBox = new TextBox();
	private Label lastNameLabel = new Label("Last name:");
	private TextBox lastNameBox = new TextBox();
	private Label nationalityLabel = new Label("Nationality:");
	private FlowPanel nationalityPanel = new FlowPanel();
	private Label icPassportLabel = new Label(IC_TEXT);
	private TextBox icPassportBox = new TextBox();
	private Label countryLabel = new Label("Country:");
	private ListBox countryBox = new ListBox();
	private Label phoneNoLabel = new Label("Phone no.:");
	private TextBox phoneNoBox = new TextBox();
	private Label emailLabel = new Label("Email address:");
	private TextBox emailBox = new TextBox();
	private Label genderLabel = new Label("Gender:");
	private HorizontalPanel genderPanel = new HorizontalPanel();
	private Label birthDateLabel = new Label("Date of Birth:");
	private DateBox birthDateBox = new DateBox();
	private Label usernameLabel = new Label("Choose a username:");
	private TextBox usernameBox = new TextBox();
	private CheckBox passUpdateCheckBox = new CheckBox(
			"Update the password too");
	private Label passwordLabel = new Label("Choose a password:");
	private PasswordTextBox passwordBox = new PasswordTextBox();
	private Label passRepLabel = new Label("Re-enter password:");
	private PasswordTextBox passRepBox = new PasswordTextBox();

	private FlexTable buttonPanel = new FlexTable();
	private Anchor mapsAnchor = new Anchor(
			"Click here to define the commuting route...");
	private HTML linkLabel1 = new HTML(
			"<b>Then paste the link </b>in the following box (");
	private Anchor linkLabel2 = new Anchor("Which link?");
	private Label linkLabel3 = new Label("):");
	private TextArea linkArea = new TextArea();
	private Image greenCheckImage = new Image("images/green-check.png");
	private Button checkRouteButton = new Button("Check the route");
	private Button resetButton = new Button("Reset");

	private MapWidget mapWidget = new MapWidget();
	private DirectionsPanel directionsPanel = new DirectionsPanel();

	private Button saveButton = new Button("Register");
	private Button cancelButton = new Button("Cancel");

	private State state;

	//

	public ProfileDetailsPanel() {
		super(Unit.EM);
		setState(State.NEW);
	}

	public void setState(State state) {
		this.state = state;
		switch (state) {
		case NEW:
			firstNameBox.setReadOnly(false);
			lastNameBox.setReadOnly(false);
			malaysianButton.setEnabled(true);
			otherButton.setEnabled(true);
			icPassportBox.setReadOnly(false);
			countryBox.setEnabled(true);
			maleButton.setEnabled(true);
			femaleButton.setEnabled(true);
			birthDateBox.setEnabled(true);

			passUpdateCheckBox.setVisible(false);
			passwordBox.setReadOnly(false);
			passRepBox.setReadOnly(false);

			saveButton.setText("Register");
			cancelButton.setVisible(false);
			break;

		case UPDATE:
			firstNameBox.setReadOnly(true);
			lastNameBox.setReadOnly(true);
			malaysianButton.setEnabled(false);
			otherButton.setEnabled(false);
			icPassportBox.setReadOnly(true);
			countryBox.setEnabled(false);
			maleButton.setEnabled(false);
			femaleButton.setEnabled(false);
			birthDateBox.setEnabled(false);

			passUpdateCheckBox.setVisible(true);
			passwordBox.setReadOnly(!passUpdateCheckBox.getValue());
			passRepBox.setReadOnly(!passUpdateCheckBox.getValue());

			saveButton.setText("Update");
			cancelButton.setVisible(true);
			break;
		}
	}

	@Override
	protected void onLoad() {
		super.onLoad();
		initProfileInfoPanel();
		initCommutingHabitsPanel();

		FlexTable personalInfoTable = createPersonalInfoTable();

		Panel routeTable = createRouteTable();
		CaptionPanel routePanel = new CaptionPanel( //
				"<h3>Commuting route</h3>", true);
		routePanel.setContentWidget(routeTable);

		FlexTable loginTable = createLoginTable();
		CaptionPanel loginPanel = new CaptionPanel( //
				"<h3>Login Info.</h3>", true);
		loginPanel.setContentWidget(loginTable);
		//

		CaptionPanel personalInfoPanel = new CaptionPanel(
				"<h3>Personal information</h3>", true);
		personalInfoPanel.setContentWidget(personalInfoTable);

		// Putting Profile and Habits panels side by side
		FlexTable innerTable = new FlexTable();
		FlexCellFormatter innerTableFormatter = innerTable
				.getFlexCellFormatter();
		innerTable.setCellSpacing(5);

		innerTable.setWidget(0, 0, routePanel);
		innerTable.setWidget(1, 0, loginPanel);
		innerTable.setWidget(0, 1, personalInfoPanel);
		innerTable.setWidget(3, 1, buttonPanel);
		innerTableFormatter.setRowSpan(1, 0, 3);
		innerTableFormatter.setRowSpan(0, 1, 3);
		innerTableFormatter.setColSpan(0, 1, 2);
		innerTableFormatter.setHeight(1, 0, "100%");
		// innerTableFormatter.setHeight(0, 0, "100%");
		// innerTableFormatter.setHeight(1, 1, "100%");
		innerTableFormatter.setVerticalAlignment(0, 0,
				HasVerticalAlignment.ALIGN_TOP);
		innerTableFormatter.setVerticalAlignment(1, 0,
				HasVerticalAlignment.ALIGN_TOP);
		innerTableFormatter.setVerticalAlignment(0, 1,
				HasVerticalAlignment.ALIGN_TOP);
		innerTableFormatter.setVerticalAlignment(3, 1,
				HasVerticalAlignment.ALIGN_TOP);

		//

		for (Label label : labels)
			formatLabel(label);

		// dockPanel.add(decoratorPanel, DockLayoutPanel.CENTER);
		this.add(innerTable);

		// buttonPanel.getElement().getStyle().setBackgroundColor(LIGHT_BLUE);
		personalInfoPanel.getElement().getStyle().setMarginLeft(1, Unit.EM);
	}

	public void setCommuter(CommuterDto commuter) {
		this.commuterDto = commuter;
		birthDateBox.setValue(commuter.getBirthDate());
		firstNameBox.setText(commuterDto.getFirstName());
		lastNameBox.setText(commuterDto.getLastName());
		icPassportBox.setText(commuterDto.getIcPassport());

		for (int i = 0; i < countryBox.getItemCount(); i++)
			if (countryBox.getValue(i).equals(commuter.getIssuingCountry())) {
				countryBox.setSelectedIndex(i);
				break;
			}
		emailBox.setText(commuterDto.getEmail());
		maleButton.setValue(commuterDto.isMale());
		femaleButton.setValue(!commuterDto.isMale());
		usernameBox.setText(commuterDto.getUsername());
		phoneNoBox.setText(commuterDto.getPhoneNo());
	}

	public CommuterDto getCommuter() {
		commuterDto.setBirthDate(birthDateBox.getValue());
		commuterDto.setFirstName(firstNameBox.getText().trim());
		commuterDto.setLastName(lastNameBox.getText().trim());
		commuterDto.setIcPassport(icPassportBox.getText().trim());

		String country = countryBox.getValue( //
				countryBox.getSelectedIndex()).trim();
		commuterDto.setIssuingCountry(country);

		commuterDto.setEmail(emailBox.getText().trim());
		commuterDto.setMale(maleButton.getValue());
		commuterDto.setUsername(usernameBox.getText());
		commuterDto.setPasswordMd5(passwordBox.getText());
		commuterDto.setPhoneNo(phoneNoBox.getText().trim());
		return commuterDto;
	}

	private void initCommutingHabitsPanel() {
		// Google Maps' "Get Directions" for Kuala Lumpur
		String mapsUrl = "http://www.google.com.my/maps?ie=UTF8&ll=3.139003,101.686855&spn=0.296871,0.441513&z=11";

		mapsAnchor.setHref(mapsUrl);
		mapsAnchor.setTarget("_new");

		linkArea.setVisibleLines(2);
		linkArea.setWidth("30em");

		linkArea.setReadOnly(false);
		checkRouteButton.setEnabled(true);
		greenCheckImage.setVisible(false);

		resetButton.addClickHandler(new ClickHandler() {
			@Override
			public void onClick(ClickEvent event) {
				linkArea.setReadOnly(false);
				checkRouteButton.setEnabled(true);
				greenCheckImage.setVisible(false);
				linkArea.setText(null);
			}
		});

		checkRouteButton.addClickHandler(new ClickHandler() {

			final Geocoder geocoder = Util.createGeocoder();
			final DirectionQueryOptions dirOptions = new DirectionQueryOptions(
					mapWidget, directionsPanel);

			@Override
			public void onClick(ClickEvent event) {
				mapWidget.clearOverlays();
				final String link = linkArea.getText().trim();
				if (link.isEmpty()) {
					Window.alert("No route has been specified!\n"
							+ "You should use Google Maps' Get Directions\n"
							+ "to generate a link ant past it in the box.");
					return;
				}
				String logMsg = "Decoded URL by GWT: " + URL.decode(link);
				Log.debug(logMsg);

				String queryStr = link.substring(link.indexOf('?') + 1);
				Log.debug("Query substring: " + queryStr);

				HashMap<String, String> paramMap = Util
						.extractParameters(queryStr);

				final RouteConfirmDialog confirmDialog = new RouteConfirmDialog( //
						mapWidget, directionsPanel);
				// confirmDialog.setSize("90%", "90%");
				// confirmDialog.show();
				confirmDialog.center();
				DeferredCommand.addCommand(new Command() {
					@Override
					public void execute() {
						mapWidget.checkResize();
					}
				});

				final DirectionsCallback dirCallback //
				= new MyDirectionsCallback(link, confirmDialog);

				String query = paramMap.get("q");
				if (query != null) {
					loadDirectionsFromQuery(query, dirCallback);

				} else {
					String saddr = paramMap.get("saddr");
					String daddr = paramMap.get("daddr");
					String[] destAddrs = daddr.split(" to:");

					String[] addrs = new String[destAddrs.length + 1];
					addrs[0] = saddr;
					System.arraycopy(destAddrs, 0, addrs, 1, destAddrs.length);

					Waypoint[] waypoints = new Waypoint[addrs.length];
					loadDirections(addrs, dirCallback, waypoints);
				}
			}

			private void loadDirectionsFromQuery( //
					String query, DirectionsCallback dirCallback) {

				Directions.load( // "from:Bukit Jalil to:3.104521,101.725159 to:Bukit Bintang",
						query, dirOptions, dirCallback);
			}

			private void loadDirections(
					//
					final String[] addrs, final DirectionsCallback dirCallback,
					final Waypoint[] waypoints) {

				checkWaypointsThenLoad(0, waypoints, addrs, dirCallback);
			}

			private void checkWaypointsThenLoad(final int index,
					final Waypoint[] waypoints, final String[] addrs,
					final DirectionsCallback dirCallback) {

				if (index < waypoints.length && waypoints[index] == null) {
					Util.createGeocoder().getLocations(addrs[index],
							new LocationCallback() {
								@Override
								public void onSuccess(
										JsArray<Placemark> locations) {
									Log.debug("Geocoding for '" + addrs[index]
											+ "' ...");
									for (int j = 0; j < locations.length(); j++)
										Log.debug("Geocoded as: "
												+ locations.get(j).getPoint());
									LatLng bestMatch = locations.get(0)
											.getPoint();
									Log.debug("WayPoint #" + index + ": "
											+ bestMatch);

									waypoints[index] = new Waypoint(bestMatch);
									Timer timer = new Timer() {
										@Override
										public void run() {
											checkWaypointsThenLoad(index + 1,
													waypoints, addrs,
													dirCallback);
										}
									};
									timer.schedule(500);
								}

								@Override
								public void onFailure(int statusCode) {
									String message = "ERRRROR while geocoding: !"
											+ StatusCodes.getName(statusCode)
											+ " (index #" + index + ")";
									RuntimeException ex = new RuntimeException(
											message);
									Log.error(message, ex);
								}
							});
				} else {
					Directions.loadFromWaypoints(waypoints, dirOptions,
							dirCallback);
				}

			}
		});
	}

	private void initProfileInfoPanel() {
		initFirstNameInput();
		initLastNameInput();
		initNationalityInput();
		initIcPassportInput();
		initCountryInput();
		initPhoneNoInput();
		initEmailInput();
		initGenderInput();
		initBirthDateInput();
		initUsernameInput();
		initPasswordInput();
		initPassRepInput();

		initButtonPanel();
	}

	public void requestFocus() {
		firstNameBox.setFocus(true);
	}

	private FlexTable createLoginTable() {
		FlexTable table = new FlexTable();
		table.getElement().getStyle().setMargin(0.5, Unit.EM);
		table.getElement().getStyle().clearMarginTop();
		table.setCellSpacing(4);

		table.setWidget(0, 0, usernameLabel);
		table.setWidget(0, 1, usernameBox);
		table.setWidget(1, 0, passUpdateCheckBox);
		table.setWidget(2, 0, passwordLabel);
		table.setWidget(2, 1, passwordBox);
		table.setWidget(3, 0, passRepLabel);
		table.setWidget(3, 1, passRepBox);

		table.getFlexCellFormatter().setColSpan(1, 0, 2);
		return table;
	}

	private Panel createRouteTable() {
		FlowPanel routeVPanel = new FlowPanel();
		routeVPanel.getElement().getStyle().setMargin(0.5, Unit.EM);
		routeVPanel.getElement().getStyle().clearMarginTop();
		routeVPanel.add(mapsAnchor);

		FlowPanel linkLablePanel = new FlowPanel();
		linkLablePanel.add(linkLabel1);
		linkLablePanel.add(linkLabel2);
		linkLablePanel.add(linkLabel3);

		routeVPanel.add(linkLablePanel);
		FlowPanel h1Panel = new FlowPanel();
		h1Panel.add(linkArea);
		h1Panel.add(greenCheckImage);
		routeVPanel.add(h1Panel);

		FlowPanel h2Panel = new FlowPanel();
		h2Panel.add(checkRouteButton);
		h2Panel.add(resetButton);
		routeVPanel.add(h2Panel);

		linkLabel1.getElement().getStyle().setFloat(Float.LEFT);
		linkLabel2.getElement().getStyle().setFloat(Float.LEFT);
		linkLabel3.getElement().getStyle().setFloat(Float.LEFT);
		//
		linkArea.getElement().getStyle().setFloat(Float.LEFT);
		greenCheckImage.getElement().getStyle().setFloat(Float.LEFT);

		checkRouteButton.getElement().getStyle().setFloat(Float.LEFT);
		resetButton.getElement().getStyle().setFloat(Float.LEFT);

		linkLabel1.getElement().getStyle().setMarginTop(0.5, Unit.EM);
		linkLabel2.getElement().getStyle().setMarginTop(0.5, Unit.EM);
		linkLabel3.getElement().getStyle().setMarginTop(0.5, Unit.EM);

		linkArea.getElement().getStyle().setMarginTop(0.5, Unit.EM);
		greenCheckImage.getElement().getStyle().setMargin(0.5, Unit.EM);

		checkRouteButton.getElement().getStyle().setMarginTop(0.5, Unit.EM);
		resetButton.getElement().getStyle().setMarginTop(0.5, Unit.EM);
		resetButton.getElement().getStyle().setMarginLeft(1, Unit.EM);

		return routeVPanel;
	}

	private FlexTable createPersonalInfoTable() {
		FlexTable table = new FlexTable();
		table.getElement().getStyle().setMargin(0.5, Unit.EM);
		table.getElement().getStyle().clearMarginTop();
		table.setCellSpacing(4);

		table.setWidget(0, 0, firstNameLabel);
		table.setWidget(0, 1, firstNameBox);
		table.setWidget(1, 0, lastNameLabel);
		table.setWidget(1, 1, lastNameBox);
		table.setWidget(2, 0, nationalityLabel);
		table.setWidget(2, 1, nationalityPanel);
		table.setWidget(3, 0, icPassportLabel);
		table.setWidget(3, 1, icPassportBox);
		table.setWidget(4, 0, countryLabel);
		table.setWidget(4, 1, countryBox);
		table.setWidget(5, 0, phoneNoLabel);
		table.setWidget(5, 1, phoneNoBox);
		table.setWidget(6, 0, emailLabel);
		table.setWidget(6, 1, emailBox);
		table.setWidget(7, 0, genderLabel);
		table.setWidget(7, 1, genderPanel);
		table.setWidget(8, 0, birthDateLabel);
		table.setWidget(8, 1, birthDateBox);
		return table;
	}

	private void initButtonPanel() {
		saveButton.addClickHandler(new ClickHandler() {
			@Override
			public void onClick(ClickEvent event) {
				if (!validateFields())
					return;
				switch (state) {
				case NEW:
					createCommuter();
					break;

				case UPDATE:
					updateCommuter();
					break;

				}

				// TODO: Insert the date of membership on the server-side

				// TODO: Don't add the commuterDto if it's already in the table.

				// TODO: Add a button to unregister this commuterDto.
			}
		});
		cancelButton.addClickHandler(new ClickHandler() {
			@Override
			public void onClick(ClickEvent event) {
				// TODO Auto-generated method stub
			}
		});
		buttonPanel.setWidth("100%");
		buttonPanel.setHeight("100%");
		buttonPanel.setCellSpacing(10);
		buttonPanel.setWidget(0, 1, saveButton);
		buttonPanel.setWidget(0, 2, cancelButton);
		FlexCellFormatter flexCellFormatter = buttonPanel
				.getFlexCellFormatter();
		flexCellFormatter.setWidth(0, 0, "100%");
		flexCellFormatter.setHeight(0, 2, "100%");
		flexCellFormatter.setVerticalAlignment(0, 2,
				HasVerticalAlignment.ALIGN_TOP);
	}

	private void initPassRepInput() {
		labels.add(passRepLabel);
		passRepBox.setWidth("12em");
	}

	private void initPasswordInput() {
		labels.add(passwordLabel);
		passwordBox.setWidth("12em");
		passUpdateCheckBox
				.addValueChangeHandler(new ValueChangeHandler<Boolean>() {
					@Override
					public void onValueChange(ValueChangeEvent<Boolean> event) {
						passwordBox.setReadOnly(!event.getValue());
						passRepBox.setReadOnly(!event.getValue());
					}
				});
	}

	private void initUsernameInput() {
		labels.add(usernameLabel);
		usernameBox.setWidth("12em");
	}

	private void initBirthDateInput() {
		labels.add(birthDateLabel);
		DateTimeFormat fullDateFormat = DateTimeFormat.getLongDateFormat();
		birthDateBox.setFormat(new DefaultFormat(fullDateFormat));

		Date initialDate = new Date(1980, 1, 1);
		birthDateBox.getDatePicker().setValue(initialDate);
	}

	private void initGenderInput() {
		labels.add(genderLabel);

		genderPanel.setSpacing(5);
		maleButton.setValue(true);
		genderPanel.add(maleButton);
		genderPanel.add(femaleButton);
	}

	private void initEmailInput() {
		labels.add(emailLabel);
	}

	private void initPhoneNoInput() {
		labels.add(phoneNoLabel);
	}

	private void initCountryInput() {
		labels.add(countryLabel);

		javaSvc.getCountries(ClientSessionManager.getId(),
				new AsyncCallback<Map<String, String>>() {
					@Override
					public void onSuccess(Map<String, String> result) {
						for (Entry<String, String> entry : result.entrySet()) {
							countryBox
									.addItem(entry.getKey(), entry.getValue());
						}
					}

					@Override
					public void onFailure(Throwable caught) {
						Log.error(caught.getMessage(), caught);
					}
				});

		countryLabel.setVisible(false);
		countryBox.setVisible(false);
	}

	private void initIcPassportInput() {
		labels.add(icPassportLabel);
	}

	private void initNationalityInput() {
		labels.add(nationalityLabel);

		// nationalityPanel.setSpacing(5);
		malaysianButton.setValue(true);
		nationalityPanel.add(malaysianButton);
		malaysianButton.getElement().getStyle().setFloat(Float.LEFT);

		nationalityPanel.add(otherButton);
		nationalityPanel.getElement().getStyle().setFloat(Float.LEFT);

		final Command updateCmd = new Command() {
			@Override
			public void execute() {
				boolean malaysian = malaysianButton.getValue();
				icPassportLabel.setText(malaysian ? IC_TEXT : PASSPORT_TEXT);
				countryLabel.setVisible(!malaysian);
				countryBox.setVisible(!malaysian);
			}
		};
		ValueChangeHandler<Boolean> radioBtnValueHandler = new ValueChangeHandler<Boolean>() {
			@Override
			public void onValueChange(ValueChangeEvent<Boolean> event) {
				if (event.getValue()) {
					DeferredCommand.addCommand(updateCmd);
				}
			}
		};
		FocusHandler radioBtnFocusHandler = new FocusHandler() {
			@Override
			public void onFocus(FocusEvent event) {
				DeferredCommand.addCommand(updateCmd);
			}
		};
		malaysianButton.addValueChangeHandler(radioBtnValueHandler);
		malaysianButton.addFocusHandler(radioBtnFocusHandler);
		otherButton.addValueChangeHandler(radioBtnValueHandler);
		otherButton.addFocusHandler(radioBtnFocusHandler);
	}

	private void initLastNameInput() {
		labels.add(lastNameLabel);
	}

	private void initFirstNameInput() {
		labels.add(firstNameLabel);
	}

	private void formatLabel(Label label) {
		Style style = label.getElement().getStyle();
		style.setFontWeight(FontWeight.BOLD);
		label.setHorizontalAlignment(Util
				.getReverseAlignment(HasAlignment.ALIGN_DEFAULT));
	}

	private void updateCommutingHabits(final DirectionResults result) {
		List<CommutingHabitDto> habits = new LinkedList<CommutingHabitDto>();

		CommutingHabitDto habit = new CommutingHabitDto();
		habit.setCommuter(commuterDto);

		List<Placemark> marks = result.getPlacemarks();

		LatLng org = marks.get(0).getPoint();
		habit.setOrigin(Util.toLatLon(org));

		LatLng dst = marks.get(marks.size() - 1).getPoint();
		habit.setDestination(Util.toLatLon(dst));

		ArrayList<HabitWaypointDto> wayPoints = extractVertices(result);
		habit.setWaypoints(wayPoints);

		habits.add(habit);
		commuterDto.setCommutingHabits(habits);
	}

	private ArrayList<HabitWaypointDto> extractVertices(
			final DirectionResults result) {

		ArrayList<HabitWaypointDto> waypoints = new ArrayList<HabitWaypointDto>();
		Polyline polyline = result.getPolyline();
		for (int i = 0; i < polyline.getVertexCount(); i++) {
			HabitWaypointDto waypoint = new HabitWaypointDto();
			waypoint.setSequenceNo(i);
			LatLng vertex = polyline.getVertex(i);
			waypoint.setPosition(Util.toLatLon(vertex));
			waypoints.add(waypoint);
		}
		return waypoints;
	}

	//

	private boolean validateFields() {
		String email = emailBox.getText().trim();
		if (email.length() == 0) {
			Window.alert("Email is required.");
			emailBox.setFocus(true);
			return false;

		} else {
			String password = passwordBox.getValue();
			String passRep = passRepBox.getValue();
			if (!password.equals(passRep)) {
				Window.alert( //
						"The password and its repetition must be identical.");
				passwordBox.setFocus(true);
				return false;

			} else {
				Date birthDate = birthDateBox.getValue();
				Date maxDate = new Date();
				maxDate.setYear(maxDate.getYear() - 12);
				if (birthDate == null) {
					Window.alert( //
							"The date of birth is not valid.");
					birthDateBox.setFocus(true);
					return false;

				} else if (maxDate.before(birthDate)) {
					Window
							.alert("Sorry! Due to safety concerns, currently we're\n"
									+ "not serving commuters below the age of 12. ");
					birthDateBox.setFocus(true);
					return false;
				}
			}
		}
		return true;
	}

	private void createCommuter() {
		commuterSvc.createCommuter(
				//
				ClientSessionManager.getId(), getCommuter(),
				new AsyncCallback<String>() {

					@Override
					public void onFailure(Throwable caught) {
						Log.error(caught.getMessage(), caught);
					}

					@Override
					public void onSuccess(final String result) {
						javaSvc.decodeKey(result, new AsyncCallback<Long>() {
							@Override
							public void onSuccess(Long result) {
								showDialog(result.toString());
							}

							@Override
							public void onFailure(Throwable caught) {
								showDialog(result);
							}

							private void showDialog(String result) {
								String message = "A commuter was successfully created"
										+ " with id = " + result;
								Log.debug(message);
								// Window.alert(message);
								DialogBox dialog = new DialogBox(true, true);
								dialog.setGlassEnabled(true);
								dialog.setText("Congratulations!");
								String //
								htmlStr = "<div style='font-size: bigger; margin: 2em;'>"
										+ message + "</div>";
								dialog.setWidget(new HTML(htmlStr));
								dialog.center();
							}

						});
					}
				});
	}

	private void updateCommuter() {
		commuterSvc.updateCommuter(
		//
				ClientSessionManager.getId(), getCommuter(), passUpdateCheckBox
						.getValue(), new AsyncCallback<Void>() {
					@Override
					public void onFailure(Throwable caught) {
						Log.error(caught.getMessage(), caught);
					}

					@Override
					public void onSuccess(Void result) {
						String message = "The details were successfully updated";
						Log.debug(message);
						Window.alert(message);
					}
				});
	}

	private final class MyDirectionsCallback implements DirectionsCallback {
		private final String link;
		private final RouteConfirmDialog confirmDialog;

		private MyDirectionsCallback( //
				String link, RouteConfirmDialog confirmDialog) {

			this.link = link;
			this.confirmDialog = confirmDialog;
		}

		@Override
		public void onSuccess(final DirectionResults result) {
			String logMsg = "ENTERING onSuccess" + " OF loadFromWaypoints";
			Log.debug(logMsg);

			Util.logPolyline(result);
			Util.logSteps(result);

			CloseHandler<PopupPanel> closeHandler = new CloseHandler<PopupPanel>() {
				@Override
				public void onClose(CloseEvent<PopupPanel> event) {

					switch (confirmDialog.getResult()) {
					case CONFIRM:
						linkArea.setReadOnly(true);
						checkRouteButton.setEnabled(false);
						greenCheckImage.setVisible(true);

						updateCommutingHabits(result);
						break;
					case MODIFY:
						Window.open(link, "_new", null);
						break;
					}
				}

			};
			confirmDialog.addCloseHandler(closeHandler);

			Log.debug("EXITING onSuccess" //
					+ " OF loadFromWaypoints");
		}

		@Override
		public void onFailure(int statusCode) {
			String message = StatusCodes.getName(statusCode);
			RuntimeException ex = new RuntimeException(message);
			Log.error(message, ex);

			Window.alert(message);
		}
	}
}
